/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "client.h"


static const char	*cl_svcOps[256] = {
	"SVC_BAD",
	"SVC_NOP",
	"SVC_SERVERSTATE",
	"SVC_GAMESTATE",
	"SVC_DOWNLOAD",
	"SVC_SNAPSHOT",
	"SVC_COMMAND",
	"SVC_EOF"
};


/*
 ==================
 CL_ParseServerState
 ==================
*/
static void CL_ParseServerState (ByteBuffer *msg){

	char	baseGame[MAX_OSPATH], game[MAX_OSPATH];
	int		protocol;
	int		i;

	// If the game module is loaded, it means the server is changing levels
	if (cg){
		Com_Printf("Changing level...\n");

		// Not active anymore, but not disconnected
		cls.state = CS_CONNECTED;

		// Shutdown game module
		CG_Shutdown();
	}

	// Wipe the entire clientActive_t structure
	memset(&cl, 0, sizeof(clientActive_t));

	// Get the server id
	cl.serverId = msg->ReadLong();

	// Get the server framerate
	cl.serverFPS = msg->ReadByte();

	// Get the server info string
	msg->ReadString(cl.serverInfo, sizeof(cl.serverInfo));

	// Get the pure mode and pack checksums
	cl.serverPure = msg->ReadBool();

	for (i = 0; i < MAX_PACK_FILES; i++)
		cl.serverPackChecksums[i] = msg->ReadLong();

	// Get the base game and current game directories
	msg->ReadString(baseGame, sizeof(baseGame));
	msg->ReadString(game, sizeof(game));

	CG_Init();
	
	CM_FreeMap();

	// Shutdown the subsystems
	CL_ShutdownAll();

	// Set the base game and current game directories
	CVar_SetVariableString("fs_baseGame", baseGame);
	CVar_SetVariableString("fs_game", game);

	// Restart the file system, setting pure mode if needed
	FS_Restart(cl.serverPure, cl.serverPackChecksums);

	// Initialize the subsystems
	CL_InitAll();

	// Enter the download state
	//CL_RequestDownloads();
}

/*
 ==================
 CL_ParseGameState
 ==================
*/
static void CL_ParseGameState (ByteBuffer *msg){

	int		protocol;
	int		offset;

	if(cg)return;

	CG_Init();
	
	CM_FreeMap();

	// Shutdown the subsystems
	CL_ShutdownAll();

	// Restart the file system, setting pure mode if needed
	FS_Restart(cl.serverPure, cl.serverPackChecksums);

	// Initialize the subsystems
	CL_InitAll();

	CG_LoadLevel();

	cls.state=CS_ACTIVE;
	// Parse the game state and request any remaining data
	//offset = cg->ParseGameState(msg);
}

/*
 ==================
 CL_ParseSnapshot
 ==================
*/
static void CL_ParseSnapshot (ByteBuffer *msg){

	int		time;
	bool	deltaCompressed;

	// Parse the snapshot
	if (!cg->ParseSnapshot(0, msg, &time, &deltaCompressed))
		return;

	// We have successfully received a valid snapshot
	cl.snapshotAvailable = true;

	cl.snapshotValid = true;
	cl.snapshotTime = time;

}

/*
 ==================
 CL_ParseCommand
 ==================
*/
static void CL_ParseCommand (ByteBuffer *msg){

	char		command[MAX_STRING_LENGTH];
	int			sequence;
	const char	*name;

	sequence = msg->ReadLong();
	msg->ReadString(command, sizeof(command));

	Com_DPrintf("Server command: %s\n", command);

	// Execute the server command
	Cmd_TokenizeString(command);

	if (!Cmd_Argc())
		return;		// No tokens

	name = Cmd_Argv(0);

	// Check for a client level command
	if (!Str_ICompare(name, "serverInfo")){
		Str_Copy(cl.serverInfo, Cmd_Argv(1), sizeof(cl.serverInfo));
		return;
	}

	if (!Str_ICompare(name, "disconnect")){
		if (Cmd_Argc() != 1)
			Com_Error(false, "Server disconnected - %s", Cmd_Args(1, -1, false));
		else
			Com_Error(false, "Server disconnected for unknown reason");
	}

	// Pass unknown server commands to the game module
	if (cg){
		cg->ServerCommand();
		return;
	}

	Com_DPrintf("Server command ignored: %s\n", name);
}

/*
 ==================
 CL_ParseServerMessage
 ==================
*/
void CL_ParseServerMessage (ByteBuffer *msg){

	int		op;

	if (cl_showMessages->integerValue){
		Com_Printf("Size: %i, ", msg->getBytesWritten());
	}

	msg->ClearReadState();

	// Parse the message
	while (1){
		if (msg->getBitsRead() > msg->getBitsWritten()){
			//Com_Error(false, "CL_ParseServerMessage: read past end of server message");
			return;
		}

		// Parse the server op
		op = msg->ReadByte();

		if (cl_showMessages->integerValue){
			if (!cl_svcOps[op])
				Com_Printf("Offset: %5i: BAD OP %i\n", msg->readBytes - 1, op);
			else
				Com_Printf("Offset: %5i: %s\n", msg->readBytes - 1, cl_svcOps[op]);
		}
		if (op == SVC_EOF){
			break;
		}

		switch (op){
		case SVC_NOP:
			break;
		case SVC_SERVERSTATE:
			CL_ParseServerState(msg);
			break;
		case SVC_GAMESTATE:
			CL_ParseGameState(msg);
			break;
		case SVC_DOWNLOAD:
			//CL_ParseDownload(msg);
			break;
		case SVC_SNAPSHOT:
			CL_ParseSnapshot(msg);
			break;
		case SVC_COMMAND:
			CL_ParseCommand(msg);
			break;
		default:
			//Com_Error(false, "CL_ParseServerMessage: illegible server message");
			break;
		}
	}
}
